﻿using System;
using System.Collections.Generic;
//using System.Linq;
using System.Text;
using XNOgre.Graphics;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using System.Diagnostics;
using XNOgre.Scene;

namespace XNOgre.Core
{
    public abstract class MovableObject : IRenderable, IDisposable
    {
        public event EventHandler ObjectDestroyed;
        public event EventHandler ObjectAttached;
        public event EventHandler ObjectDetached;
        public event EventHandler ObjectMoved;  

        protected string name;
        protected SceneManager manager;
        protected Node parentNode;
        protected bool visible;
        protected RenderQueueGroupID renderQueueID;

        private BoundingBox worldAABB;
        private BoundingSphere worldBoundingSphere;

        private bool castsShadows;
        private bool receiveShadows;

        private Material material;

        #region C/D'tors
        public MovableObject()
        {
            material = new Graphics.Material();
            manager = null;
            parentNode = null;
            visible = true;
            renderQueueID = RenderQueueGroupID.Main;
            castsShadows = true;
            this.name = Root.GenerateName(this.GetType());
        }
        public MovableObject(string name)
        {
            material = new Graphics.Material();
            manager = null;
            parentNode = null;
            visible = true;
            renderQueueID = RenderQueueGroupID.Main;
            castsShadows = true;
            this.name = name;
        }

        public virtual void Dispose()
        {
            if (parentNode != null)
            {
                if (parentNode is SceneNode)
                {
                    (parentNode as SceneNode).DetachObject(this);
                }
            }
            if (Material != null)
            {
                Material.Dispose();
                Material = null;
            }

            if (ObjectDestroyed != null)
            {
                ObjectDestroyed(this, EventArgs.Empty);
            }
        }
        #endregion

        #region UpdatesFromParent
        internal void NotifyMoved()
        {
            if (ObjectMoved != null)
            {
                ObjectMoved(this, EventArgs.Empty);
            }
        }
        internal void NotifyAttached(Node parent)
        {
            bool different = (parent != parentNode);

            parentNode = parent;
            if (different)
            {
                if (ObjectAttached != null)
                {
                    ObjectAttached(this, EventArgs.Empty);
                }
            }
        }
        #endregion

        public void DetachFromParent()
        {
            if (IsAttached)
            {
                if (parentNode is SceneNode)
                {
                    (parentNode as SceneNode).DetachObject(this);
                }

                if (ObjectDetached != null)
                {
                    ObjectDetached(this, EventArgs.Empty);
                }
            }
        }



        #region AttributeProperties
        public bool IsAttached
        {
            get
            {
                return (parentNode != null);
            }
        }
        public string Name
        {
            get { return name; }
        }
        public virtual BoundingBox BoundingBox
        {
            get { return worldAABB; }
        }
        public virtual BoundingSphere BoundingSphere
        {
            get { return worldBoundingSphere; }
        }
        public Node ParentNode
        {
            get
            {
                return parentNode;
            }
        }
        public virtual Matrix ParentNodeFullTransform
        {
            get
            {
                if (parentNode != null)
                    return parentNode.FullTransform;

                return Matrix.Identity;
            }
        }
        public RenderQueueGroupID RenderQueueGroup
        {
            get { return renderQueueID; }
            set
            {
                renderQueueID = value;
            }
        }
        public bool Visible
        {
            get { return visible; }
            set { visible = value; }
        }
        public object Tag { get; set; }

        #endregion

        #region EngineProperties
        internal SceneManager Manager
        {
            get
            {
                return manager;
            }
            set
            {
                manager = value;
            }
        }
        #endregion


        #region IRenderable

        public virtual Material Material
        {
            get { return material; }
            set
            {
                material = value;
            }
        }

        public virtual RenderOperation RenderOperation
        {
            get
            {
                return null;
            }
        }
        internal abstract void UpdateRenderQueue(ref List<IRenderable> queue);
        internal virtual void UpdateBoundingBox()
        {
            //
        }
        public virtual Matrix WorldTransforms
        {
            get { return ParentNodeFullTransform; }
        }

        public virtual bool CastsShadows
        {
            get { return castsShadows; }
            set { castsShadows = value; }
        }
        public virtual bool ReceiveShadows
        {
            get { return receiveShadows; }
            set { receiveShadows = value; }
        }
        #endregion

        private IEnumerable<Light> lights;
        public virtual IEnumerable<Light> Lights
        {
            get
            {
                return lights;
            }
            set
            {
                List<Light> validLights = new List<Light>();
                foreach (var light in value)
                {
                    if (LightIsValid(light))
                    {
                        validLights.Add(light);
                    }
                }
                lights = validLights;
            }
        }

        public bool LightIsValid(Light l)
        {
            if (l.Type == Light.LightType.Directional)
            {
                return true;
            }
            else
            {
                if (IsAttached == false)
                    return false;

                float distance = Vector3.Distance(l.Position, this.ParentNode.Position);

                //trick the light into pointing the proper direction
                if (l.Type == Light.LightType.Point)
                {
                    l.Direction = (this.ParentNode.Position - l.Position) / distance;
                }

                return (distance <= l.AttenuationRange);
            }
        }

        public virtual void NotifyCurrentCamera(Camera cam)
        {
        }

        public virtual float GetSquaredViewDepth(Camera camera)
        {
            return 0;
        }
    }
}
